﻿using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Collections.Generic;
using System.Threading;

namespace CzfUDP
{
	// 投递方式
	public enum NetDeliveryMethod{
		/// <summary>
		/// 无序,丢包
		/// </summary>
		Unreliable = 0,
		/// <summary>
		/// 有序,丢包
		/// </summary>
		UnreliableSequenced = 1,
		/// <summary>
		/// 无序,不丢包
		/// </summary>
		ReliableUnordered = 2,
		/// <summary>
		/// 有序,不丢包
		/// </summary>
		ReliableOrdered = 3
	}

	// 数据包
	public class Packet{
		public uint num;
		public byte[] bytes;
		public long ticks;
		public long createTicks;
		public int smallLength;   // 分包大小
		public int smallNum;      // 分包序号
		public uint smallId;      // 分包Id
		public int retryTimes;    // 重试次数

		public Packet(uint num, byte[] bytes, uint smallId = 0, int smallLength = 1, int smallNum = 1)
		{
			this.retryTimes = 0;
			this.smallId = smallId;
			this.smallLength = smallLength;
			this.smallNum = smallNum;
			this.num = num;
			this.bytes = bytes;
			createTicks = ticks = DateTime.Now.Ticks;
		}
	}

	public delegate void CzfUDPAction<T1, T2>(T1 p1, T2 p2);
	public delegate void CzfUDPAction<T1, T2, T3>(T1 p1, T2 p2, T3 p3);

	// 分包组
	public class SubPackets{
		public byte[][] packets;
		public uint smallId;      // 分包Id
		public int count = 0;	  // 已经获取到的分包数量
		public long ticks;
	}

	public class CzfUDPClient
	{
		const int maxTimeout = 5000;

		#region static
		private static object _idLock = new object ();
		private static int _id = 0;
		public static int GetId()
		{
			lock (_idLock) {
				_id++;
				if (_id >= int.MaxValue)
					_id = 1;
				return _id;
			}
		}

		public static Dictionary<int, CzfUDPClient> allClients = new Dictionary<int, CzfUDPClient> ();
		public static object allClients_lock = new object ();

		public static CzfUDPClient GetClient(int id){
			lock (allClients_lock) {
				if (allClients.ContainsKey (id))
					return allClients [id];
				else
					return null;
			}
		}

		public static CzfUDPClient GetClientByRemoteId(int remote_id){
			try{
				lock(allClients_lock){
					foreach (var c in allClients.Values) {
						if (c.remote_id == remote_id)
							return c;
					}
				}
			}catch{
			}
			return null;
		}
		#endregion

		public event Action<CzfUDPClient> OnConnect;
		public event CzfUDPAction<CzfUDPClient, byte[]> OnRecive;
		public event CzfUDPAction<CzfUDPClient, byte[], byte> OnSend;
		public event Action<CzfUDPClient> OnClose;
		public event Action<string> OnDebug;

		public int id;  // 连接ID
		public int remote_id;  // 远程连接ID
		public NetDeliveryMethod netDeliveryMethod;
		public EndPoint remote_end;

		public int clientTimeout = 60000; // 超时无响应则关闭连接
		public long lastRecTick = DateTime.Now.Ticks; // 最后一次接收到包的时间

		private int mtu;
		private int timeout = 400;
		/// <summary>
		/// 平均网络延迟
		/// </summary>
		public int ARTT = 400;
		private int DRTT = 100;

		private Action<int> sleep;
		private Action<ThreadStart> newThread;

		public CzfUDPClient (int remote_id, int mtu, byte client_type, Action<ThreadStart> newThread, Action<int> sleep)
		{
			this.netDeliveryMethod = (NetDeliveryMethod)client_type;
			this.mtu = mtu;
			this.remote_id = remote_id;
			this.id = GetId ();
			this.sleep = sleep;
			this.newThread = newThread;

			lock (allClients_lock) {
				if (allClients.ContainsKey (id)) {
					allClients [id].Close ();
				}
				allClients [id] = this;
			}

			// 初始化发送队列
			packets = new Queue<Packet>();

			if (this.netDeliveryMethod == NetDeliveryMethod.ReliableOrdered ||
			   this.netDeliveryMethod == NetDeliveryMethod.ReliableUnordered) {
				// 不丢包
				// 初始化重试队列
				retryPackets = new Dictionary<uint,Packet>();
				retryPackets_lock = new object();
				// 初始化分包组缓存
				allSubPackets = new Dictionary<uint, SubPackets> ();
				allSubPackets_lock = new object ();

				if (this.netDeliveryMethod == NetDeliveryMethod.ReliableOrdered) {
					// 初始化超前包缓存
					afterPackets = new Dictionary<uint, Packet> ();
					afterPackets_lock = new object ();
				} else {
					// 初始化已处理过的数据包缓存
					actionPackets = new Dictionary<uint, Packet> ();
					actionPackets_lock = new object ();
				}
			}
		}

		// 建立连接
		public bool Connect(int retry, int rate)
		{
			int i = 0;
			while (!open && i<retry) {
				var sendBts = new byte[]{ (byte)netDeliveryMethod };
				sendBts = CzfUDPTool.AddHead (sendBts, id);
				sendBts = CzfUDPTool.AddHead (sendBts, CzfUDPTool.SYN);

				if (OnSend != null)
					OnSend (null, sendBts, 0);

				i++;
				Thread.Sleep (rate);
			}

			return open;
		}


		private Queue<Packet> packets; // 发送队列
		private object packets_lock = new object(); // 重试队列锁

		private Dictionary<uint,Packet> retryPackets; // 重试队列
		private object retryPackets_lock; // 重试队列锁

		private Dictionary<uint, Packet> actionPackets; // 已处理过的数据包
		private object actionPackets_lock; // 已处理过的数据包锁
		private int actionPacketsTimeout = 60000;  // 已处理过的数据包超时时间(ms)

		private Dictionary<uint, SubPackets> allSubPackets;  // 分包组缓存
		private object allSubPackets_lock;   // 分包组缓存锁

		private Dictionary<uint, Packet> afterPackets;  // 超前包缓存
		private object afterPackets_lock;   // 超前包缓存锁

		// 自我监控任务
		void MyStart()
		{
//			if(OnDebug != null)
//				OnDebug("MyStart:" + open);
			while(open){
				try{
					var now = DateTime.Now.Ticks;

					// 判断是否超时断开连接
//					var o = new TimeSpan(now - lastRecTick).TotalMilliseconds;
//					if(o>clientTimeout)
//					{
//						Close();
//						return;
//					}

					#region 不丢包机制下的逻辑
					if (this.netDeliveryMethod == NetDeliveryMethod.ReliableOrdered ||
						this.netDeliveryMethod == NetDeliveryMethod.ReliableUnordered) {

						// 处理重试队列
						lock(retryPackets_lock){
							foreach(var p in retryPackets.Values)
							{
								// 判断是否超时
								var cost = new TimeSpan(now - p.ticks).TotalMilliseconds;
								if(cost > (timeout << p.retryTimes) || cost > maxTimeout){
									// 超时,进入重发队列
									if(new TimeSpan(now - p.createTicks).TotalMilliseconds >= clientTimeout)
									{
										// 有数据包超时,强制关闭连接
										Close();
										return;
									}
									p.retryTimes ++;
									p.ticks = now;
									packets.Enqueue(p);

//									if(OnDebug != null)
//										OnDebug("超时重发:" + p.num);
								}
							}
						}

						if(this.netDeliveryMethod == NetDeliveryMethod.ReliableUnordered){
							// 已处理过的数据包超时判断
							lock(actionPackets_lock){
								uint[] arrKeys = new uint[actionPackets.Count];
								actionPackets.Keys.CopyTo(arrKeys, 0);
								foreach(var k in arrKeys) {
									var p = actionPackets[k];
									var cost = new TimeSpan(now - p.ticks).TotalMilliseconds;
									if(cost > actionPacketsTimeout){
										// 超时,删除
										actionPackets.Remove(k);
									}
								}
							}
						}
					}
					#endregion

					// 处理发送队列
					while (open && packets.Count > 0) {
						Packet packet = packets.Dequeue();
						Send (packet);
						// 平滑处理
						sleep(1);
					}

				}catch(Exception e)
				{
					Console.WriteLine("Error:" + e.ToString());
					throw e;
				}
				sleep(timeout >> 2);
			}
		}

		public void DebugInfo()
		{
			if (OnDebug != null) {
				OnDebug ("ARTT:" + ARTT);
				OnDebug ("DRTT:" + DRTT);
				OnDebug ("timeout:" + timeout);
				OnDebug ("发送队列:" + packets.Count);
				if (retryPackets != null) {
					OnDebug ("重试队列:" + retryPackets.Count);
				}

				if (actionPackets != null) {
					OnDebug ("已处理过的数据包:" + actionPackets.Count);
				}

				if (allSubPackets != null) {
					OnDebug ("分包组缓存:" + allSubPackets.Count);
				}

				if (afterPackets != null) {
					OnDebug ("超前包缓存:" + afterPackets.Count);
				}
			}
		}

		public void Close(){
			if (open) {
				open = false;
				lock (allClients_lock) {
					allClients.Remove (this.id);
				}
				if (OnClose != null)
					OnClose (this);
			}
		}

		// 连接建立成功
		private bool open = false;
		public void Open()
		{
			open = true;
			newThread (MyStart);
			if (OnConnect != null)
				OnConnect (this);
		}

		public void Send(byte[] bytes, byte type = CzfUDPTool.PSH){
			if (netDeliveryMethod == NetDeliveryMethod.ReliableOrdered ||
			    netDeliveryMethod == NetDeliveryMethod.ReliableUnordered) {
				// 大包情况
				if(bytes.Length + 8 > mtu){
					SendBig(bytes);
					return;
				}
			}
			uint sendNum = 0;
			if (type == CzfUDPTool.PSH)
				sendNum = GetSendNum ();
			Send (new Packet (sendNum, bytes), type);
		}

		public void Send(Packet packet, byte type = CzfUDPTool.PSH){
			if (type == CzfUDPTool.PSH && !open) {
				// 还没建立连接,将数据加入缓存队列
				packets.Enqueue (packet);
				return;
			}
			var bytes = packet.bytes;
			if (netDeliveryMethod == NetDeliveryMethod.Unreliable) {
				#region 无序,丢包
				// 不做任何处理,直接发送
				if (OnSend != null)
					OnSend (this, bytes, type);
				#endregion
			} else if (netDeliveryMethod == NetDeliveryMethod.UnreliableSequenced) {
				#region 有序,丢包
				if (type == CzfUDPTool.PSH) {
					// 需要发送用户数据,拼上序列号
					bytes = CzfUDPTool.AddHead(bytes, packet.num);
				}
				if (OnSend != null)
					OnSend (this, bytes, type);
				#endregion
			} else if (netDeliveryMethod == NetDeliveryMethod.ReliableUnordered ||
				netDeliveryMethod == NetDeliveryMethod.ReliableOrdered) {
				#region 无序,不丢包 或 有序,不丢包
				if (type == CzfUDPTool.PSH) {
					lock (retryPackets_lock) {
						// 进入重试队列
						retryPackets[packet.num] = packet;
					}

					if(packet.smallId != 0){
						// 是分包的情况
						// 拼上分包大小和分包序号
						bytes = CzfUDPTool.AddHead(bytes, packet.smallNum); 
						bytes = CzfUDPTool.AddHead(bytes, packet.smallLength); 
					}

					// 拼上分包Id
					bytes = CzfUDPTool.AddHead(bytes, packet.smallId);
					// 拼上包序号
					bytes = CzfUDPTool.AddHead(bytes, packet.num);


				}
				if (OnSend != null)
					OnSend (this, bytes, type);
				#endregion
			} else {
				throw new Exception ("未实现");
			}
		}

		public void Recive(BinaryReader ms, byte type){
			lastRecTick = DateTime.Now.Ticks;
			if (netDeliveryMethod == NetDeliveryMethod.Unreliable) {
				#region 无序,丢包
				if (type == CzfUDPTool.PSH) {
					// 不做任何处理,直接抛出数据
					if (OnRecive != null)
						OnRecive (this, GetAllBytes (ms));
				} else {
					throw new Exception ();
				}
				#endregion
			} else if (netDeliveryMethod == NetDeliveryMethod.UnreliableSequenced) {
				#region 有序,丢包
				if (type == CzfUDPTool.PSH) {
					// 收到用户数据,处理序列号
					var recNum = ms.ReadUInt32 ();
					lock (reciveNum_lock) {
						if (CheckNewNum (this.reciveNum, recNum)) {
							// 新的数据包,更新序列号
							this.reciveNum = recNum;
						} else {
							// 过期的数据包,直接丢弃
							return;
						}
					}
					if (OnRecive != null)
						OnRecive (this, GetAllBytes (ms));
				} else {
					throw new Exception ();
				}
				#endregion
			} else if (netDeliveryMethod == NetDeliveryMethod.ReliableUnordered) {
				#region 无序,不丢包
				if (type == CzfUDPTool.PSH) {
					// 获取序列号
					var recNum = ms.ReadUInt32 ();

					// 返回ACK信息
					var ack = BitConverter.GetBytes (recNum);
					Send (ack, CzfUDPTool.ACK);

					// 检查是否处理过
					lock (actionPackets_lock) {
						if (actionPackets.ContainsKey (recNum)) {
							// 已经处理过该数据包,直接忽略
							return;
						} else {
							// 没处理过,加入已处理列表
							actionPackets.Add (recNum, new Packet (recNum, null));
						}
					}

					ReceiveReliablePSH(GetAllBytes(ms));
				} else if (type == CzfUDPTool.ACK) {
					// 这是一个响应包
					ReceiveACK (ms);
				} else {
					throw new Exception ();
				}
				#endregion
			} else if (netDeliveryMethod == NetDeliveryMethod.ReliableOrdered) {
				#region 有序,不丢包
				if (type == CzfUDPTool.PSH) {
					// 获取序列号
					var recNum = ms.ReadUInt32 ();
//					if(OnDebug != null)
//						OnDebug("recNum:" + recNum);
					
					// 返回ACK信息
					var ack = BitConverter.GetBytes (recNum);
					Send (ack, CzfUDPTool.ACK);
					// 处理
					ReceiveReliableOrderedPSH(GetAllBytes(ms), recNum);
				} else if (type == CzfUDPTool.ACK) {
					// 这是一个响应包
					ReceiveACK (ms);
				}
				#endregion
			}else {
				throw new Exception ("未实现");
			}
		}

		// 处理有序不丢包机制下的数据包
		private void ReceiveReliableOrderedPSH(byte[] bytes, uint recNum)
		{
			lock (reciveNum_lock) {
				var checkNext = CheckNextNum (this.reciveNum, recNum);
				if (checkNext == 0) {
					// 正好是下一个数据包
					// 更新序列号
					this.reciveNum = recNum;
					// 处理数据包
					ReceiveReliablePSH (bytes);

					// 计算下一个包的num
					uint nextNum;
					if (recNum == uint.MaxValue)
						nextNum = 1;
					else
						nextNum = recNum + 1;
					
					// 检查是否有提前收到的下一个数据包
					lock (afterPackets_lock) {
						if (afterPackets.ContainsKey (nextNum)) {
							
							var p = afterPackets [nextNum];
							afterPackets.Remove (nextNum);
							ReceiveReliableOrderedPSH (p.bytes, nextNum);
						}
					}
				} else if (checkNext == 1) {
					// 超前的数据包,放入缓存
					lock (afterPackets_lock) {
						if(!afterPackets.ContainsKey(recNum))
							afterPackets.Add(recNum, new Packet(recNum, bytes));
					}
					return;
				} else {
					// 过期的数据包,直接丢弃
					return;
				}
			}
		}

		// 处理不丢包机制下的数据包
		private void ReceiveReliablePSH(byte[] bytes)
		{
			int postion = 0;

			// 获取分包Id
			var smallId = BitConverter.ToUInt32(bytes, postion);
			postion += 4;

			if (smallId != 0) {
				// 是分包,进入分包处理逻辑
				ReceiveBig (CzfUDPTool.Sub(bytes, postion), smallId);
			} else {
				// 不是分包,直接抛出进行处理
				if (OnRecive != null)
					OnRecive (this, CzfUDPTool.Sub(bytes, postion));
			}
		}

		// 处理响应包逻辑
		private void ReceiveACK(BinaryReader ms)
		{
			// 获取序列号
			var recNum = ms.ReadUInt32();
			lock (retryPackets_lock) {
				if(retryPackets.ContainsKey(recNum)){
					var p = retryPackets [recNum];
					if (p.retryTimes == 0) {
						var srtt = new TimeSpan (DateTime.Now.Ticks - p.ticks).TotalMilliseconds;
						ResetTimeOut ((int)srtt);
					}
					// 删除重试队列中的记录
					retryPackets.Remove (recNum);
				}
			}
		}

		#region 处理大包数据
		private void SendBig(byte[] big){
			// 计算每个小包的最大长度(mtu - 4byte的包序号 - 4byte的smallid - 4byte的smallLength - 4byte的smallNum)
			int maxBytesLength = this.mtu - 16; 

			int position = 0;
			List<byte[]> subs = new List<byte[]> ();
			// 分包
			while (position < big.Length) {
				var sub = CzfUDPTool.Sub (big, position, maxBytesLength);
				subs.Add (sub);
				position += sub.Length;
			}

			uint smallId = GetSmallId (); // 分包ID
			int smallLength = subs.Count; // 分包数量

			// 发送分包
			for (int i=1;i<=smallLength;i++) {
				var packet = new Packet (GetSendNum (), subs [i - 1], smallId, smallLength, i);
				this.Send (packet);
			}
		}

		// 分包处理逻辑
		private void ReceiveBig(byte[] big, uint smallId){
			int postion = 0;

			var smallLength = BitConverter.ToInt32(big, postion);
			postion += 4;

			var smallNum = BitConverter.ToInt32(big, postion);
			postion += 4;

			// 找到或者新建分包缓存
			SubPackets subPackets;
			lock (allSubPackets_lock) {
				if (allSubPackets.ContainsKey (smallId)) {
					subPackets = allSubPackets [smallId];
				} else {
					subPackets = new SubPackets ();
					subPackets.smallId = smallId;
					subPackets.packets = new byte[smallLength][];
					allSubPackets.Add (smallId, subPackets);
				}
			

				// 更新分包缓存
				subPackets.packets [smallNum - 1] = CzfUDPTool.Sub (big, postion); 
				subPackets.count++;

				// 判断分包全部获取成功
				if (subPackets.count == subPackets.packets.Length) {
					// 从缓存中清除
					allSubPackets.Remove (smallId);
				} else {
					// 更新时间戳
					subPackets.ticks = DateTime.Now.Ticks;
					return;
				}

				// 组成大包
				int maxBytesLength = this.mtu - 16; 
				var ms = new MemoryStream (maxBytesLength * subPackets.count);
				foreach (var bts in subPackets.packets) {
					ms.Write (bts, 0, bts.Length);
				}

				// 抛出组合完成的数据
				var btst = ms.ToArray ();
				if (OnRecive != null)
					OnRecive (this, btst);
			}
			
		}
		#endregion

		private bool CheckNewNum(uint oldNum, uint newNum)
		{
			return newNum > oldNum || ((oldNum - newNum) > (uint.MaxValue >> 1));
		}

		private int CheckNextNum(uint oldNum, uint newNum)
		{
			if (newNum == oldNum + 1 || (oldNum == uint.MaxValue && newNum == 1)) {
				return 0; // 正好是下一个包
			}
			if (!CheckNewNum (oldNum, newNum)) {
				return -1; // 过期的数据包
			} else {
				return 1; // 超前的数据包
			}
		}

		private uint sendNum = 0; // 发送序列号
		private object sendNum_lock = new object();// 发送序列号锁
		public uint SendNum {
			get {
				return sendNum;
			}
		}
 
		private uint reciveNum = 0; // 接收序列号
		private object reciveNum_lock = new object(); // 接收序列号锁
		public uint ReciveNum {
			get {
				return reciveNum;
			}
		}


		private uint smallId = 0;// 分包Id
		private object smallId_lock = new object(); // 分包Id锁

		private uint GetSmallId()
		{
			lock (smallId_lock) {
				smallId++;
				if (smallId >= uint.MaxValue)
					smallId = 1;
				return smallId;
			}
		}

		private uint GetSendNum()
		{
			lock (sendNum_lock) {
				sendNum++;
				if (sendNum >= uint.MaxValue)
					sendNum = 1;
				return sendNum;
			}
		}

		// 重新计算timeout
		private void ResetTimeOut(int srtt)
		{
			DRTT = ((DRTT * 3) >> 2) + (Math.Abs(ARTT - srtt) >> 2);
			ARTT = ((ARTT * 7) >> 3) + (srtt >> 3);
			timeout = ARTT + (DRTT << 2);
			// 最大延迟5000
			if (timeout > maxTimeout)
				timeout = maxTimeout;
			// 最小延迟10
			if (timeout <= 50) 
				timeout = 50;
		}

		private byte[] GetAllBytes(BinaryReader ms)
		{
			return ms.ReadBytes ((int)(ms.BaseStream.Length - ms.BaseStream.Position));
		}
	}
}

